﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using EnCor.Hosting;
using EnCor.Wcf.Routing;
using System.ServiceModel.Description;
using System.Xml;

namespace EnCor.Wcf.Hosting
{
    [ServiceContract(Namespace = "http://encor.codeplex.com/wcf/routing/")]
    public interface IRouterService
    {
        [OperationContract(Action = "*", ReplyAction = "*")]
        Message ProcessMessage(Message requestMessage);
    }


    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
    public class RouterService : IRouterService
    {
        public RouterService()
        { 
            
        }

        public Message ProcessMessage(Message requestMessage)
        {

            MessageBuffer messageBuffer = requestMessage.CreateBufferedCopy(int.MaxValue);
            Message redirectedRequestMessage = messageBuffer.CreateMessage();
            Message backupNextMessage = messageBuffer.CreateMessage();
            Message backupExceptionMessage = messageBuffer.CreateMessage();

            Binding binding = null;
            EndpointAddress endpointAddress = null;
            int invokeServiceHasCode;
            UniqueId messageId = requestMessage.Headers.MessageId;
            Console.WriteLine();
            Console.WriteLine(String.Format("ProcessMessage MessageId is {0}", messageId.ToString()));

            GetServiceEndpoint(requestMessage, out binding, out endpointAddress, out invokeServiceHasCode);

            using (ChannelFactory<IRouterService> factory = new ChannelFactory<IRouterService>(binding, endpointAddress))
            {
                factory.Endpoint.Behaviors.Add(new MustUnderstandBehavior(false));
                factory.Endpoint.Behaviors.Add(new ClientViaBehavior(endpointAddress.Uri));
                IRouterService proxy = factory.CreateChannel();

                using (proxy as IDisposable)
                {
                    try
                    {
                        // log request message
                        IClientChannel clientChannel = proxy as IClientChannel;
                        //Console.WriteLine(String.Format("Request received at {0}, to {1}, Action: {2}", DateTime.Now, clientChannel.RemoteAddress.Uri.AbsoluteUri, requestMessage.Headers.Action));
                        // invoke service
                        RegisterService.KeepCalledService(messageId, endpointAddress.Uri);
                        RegisterService.StartServiceInvoke(invokeServiceHasCode);
                        Message responseMessage = proxy.ProcessMessage(redirectedRequestMessage);
                        if (responseMessage.IsFault)
                        {
                            Console.WriteLine(String.Format("ResponseMessage IsFault. MessageId={0}", messageId));
                            Log4netHelper.Log.Info("RouterService.ProcessMessage(), responseMessage IsFault, MessageId=" + requestMessage.Headers.MessageId.ToString());
                            RegisterService.EndServiceInvoke(invokeServiceHasCode);
                            Console.WriteLine();
                            return ProcessMessage(backupNextMessage);
                        }

                        // log response message
                        Console.WriteLine(String.Format("Reply received at {0} , Action: {1}", DateTime.Now, responseMessage.Headers.Action));
                        Console.WriteLine();
                        RegisterService.EndServiceInvoke(invokeServiceHasCode);
                        RegisterService.RemoveCalledService(messageId);
                        return responseMessage;
                    }
                    catch (Exception ex)
                    {
                        Log4netHelper.Log.Info("RouterService.ProcessMessage(), Exception MessageId=" + requestMessage.Headers.MessageId.ToString() + "Exception:" +ex.Message, ex);

                        Console.WriteLine(String.Format("RouterService.ProcessMessage(), Exception : {0}", ex.Message));
                        RegisterService.EndServiceInvoke(invokeServiceHasCode);
                        Console.WriteLine();
                        return ProcessMessage(backupExceptionMessage);
                    }
                }
            }
        }

        private void GetServiceEndpoint(Message requestMessage, out Binding binding, out EndpointAddress endpointAddress, out int invokeServiceHasCode)
        {
            RegisterService.GetServiceEndpoint(requestMessage, out binding, out endpointAddress, out invokeServiceHasCode, requestMessage.Headers.MessageId);
        }    
    }
}
